package api

import (
	"crypto/ecdsa"
	"fmt"
	"go-eth-tracker/dao"
	"go-eth-tracker/models"
	"strings"
	"time"

	"github.com/btcsuite/btcutil/hdkeychain"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/crypto"
	"go.uber.org/zap"
)

type AddressHandler struct {
	MssqlDao     *dao.MssqlDao
	LogContext   *zap.Logger
	MasterPubKey string
	ApiContext   *Context
}

type AddressRequest struct {
	UserId string `json:"userId"`
}

type Address struct {
	Schema         string `json:"schema"`
	ReceiveAddress string `json:"address"`
}

func (h *AddressHandler) GetAddress(merchantId string, userId string) *Address {
	h.LogContext.Info(fmt.Sprintf("为商户 %s 的用户 %s 获取收款地址", merchantId, userId))

	merchant := h.ApiContext.GetMerchant(merchantId)
	fmt.Println("[NewKeyFromString]start")

	mchDerivePubKey := merchant.MasterDerivationStrategy
	if strings.Index(mchDerivePubKey, "-[legacy]") > 0 {
		mchDerivePubKey = strings.Replace(mchDerivePubKey, "-[legacy]", "", -1)
	}
	masterExtPubKey, err := hdkeychain.NewKeyFromString(mchDerivePubKey)
	if err != nil {
		h.LogContext.Fatal(fmt.Sprintf("[%s]NewKeyFromString parse error,  %s", mchDerivePubKey, err.Error()))
	}
	h.LogContext.Info(fmt.Sprintf("masterExtPubKey: %#v", masterExtPubKey))
	nextChildIndex := h.MssqlDao.GetNextDerivationIndex(mchDerivePubKey)
	strKeyPath := fmt.Sprintf("0/%d/%s", merchant.Id, time.Now().Format("0601"))
	strSubKeyPath := fmt.Sprintf("0/%d", nextChildIndex)
	strFullKeyPath := fmt.Sprintf("%s/%s", strKeyPath, strSubKeyPath)

	h.LogContext.Info(fmt.Sprintf("%s => %d (%s)", mchDerivePubKey, nextChildIndex, strFullKeyPath))
	derivePath, err := accounts.ParseDerivationPath(strFullKeyPath)
	if err != nil {
		h.LogContext.Fatal(fmt.Sprintf("[%d@%s]Child  get error, %s", nextChildIndex, mchDerivePubKey, err.Error()))
	}

	h.LogContext.Info(fmt.Sprintf("[%d@%s]Child  get success: %s, default:%s", nextChildIndex, mchDerivePubKey, derivePath.String(), accounts.DefaultRootDerivationPath.String()))
	nextECDSAPubKey, err := h.derivePublicKey(masterExtPubKey, derivePath)
	if err != nil {
		h.LogContext.Fatal(fmt.Sprintf("[%d@%s]DerivePublicKey, %s", nextChildIndex, mchDerivePubKey, err.Error()))
	}
	nextAddress := crypto.PubkeyToAddress(*nextECDSAPubKey)
	payAddress := nextAddress.Hex()

	addr := &models.BTCLikeAddress{
		Id:                 0,
		CryptoCode:         "ETH",
		PayAddress:         payAddress,
		ScriptPubKeyHash:   nextAddress.Hash().Hex(),
		DerivationStrategy: mchDerivePubKey,
		KeyPath:            strKeyPath,
		SubKeyPath:         strSubKeyPath,
		UserId:             userId,
		MerchantId:         merchantId,
		CreateTime:         time.Now(),
		Property:           0,
	}
	h.MssqlDao.InsertAddress(addr)

	return &Address{
		Schema:         "ethereum",
		ReceiveAddress: payAddress,
	}
}

// DerivePublicKey derives the public key of the derivation path.
func (h *AddressHandler) derivePublicKey(extPubKey *hdkeychain.ExtendedKey, path accounts.DerivationPath) (*ecdsa.PublicKey, error) {
	var err error
	key := extPubKey

	for i, n := range path {
		// i > 3 是自定义的 DerivePath
		if i > 3 {
			key, err = key.Child(n)
			if err != nil {
				return nil, err
			}
		}
	}

	ecPubKey, err := key.ECPubKey()
	if err != nil {
		return nil, err
	}

	publicKeyECDSA := ecPubKey.ToECDSA()
	return publicKeyECDSA, nil
}
